// 定义一个接口 IndexableWindow，允许通过字符串键访问 window 对象的任意属性
interface IndexableWindow {
	/* eslint-disable @typescript-eslint/no-explicit-any */
	[key: string]: any;
}

// 将 window 对象类型断言为 IndexableWindow，以便可以访问非标准属性
const win = window as IndexableWindow;

// 导出一个名为 windowInit 的函数，用于初始化 requestAnimationFrame 和 cancelAnimationFrame
export function windowInit() {
	// 记录上次调用的时间戳
	let lastTime = 0;
	// 定义浏览器供应商前缀数组，用于处理不同浏览器的兼容性问题
	const vendors = ['webkit', 'moz'];
	// 遍历供应商前缀数组，当前浏览器不支持原生的 requestAnimationFrame 时，
	// 		尝试为 window.requestAnimationFrame 和 window.cancelAnimationFrame 分配兼容版本的方法
	for (let x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
		// 尝试从 window 对象中获取带前缀的 requestAnimationFrame 方法
		window.requestAnimationFrame = win[vendors[x] + 'RequestAnimationFrame'];
		// 尝试从 window 对象中获取带前缀的 cancelAnimationFrame 方法
		// 注意：在 Webkit 浏览器中，方法名可能为 cancelRequestAnimationFrame
		window.cancelAnimationFrame =
			win[vendors[x] + 'CancelAnimationFrame'] || // name has changed in Webkit
			win[vendors[x] + 'CancelRequestAnimationFrame'];
	}

	/*
		只要动画帧的刷新率符合显示器的刷新率（通常是每秒 60 帧），并且开发者使用了 requestAnimationFrame 来控制动画，
			那么每一帧都会执行一次 requestAnimationFrame 的回调函数。
		关键点：
			动画帧同步：requestAnimationFrame 确保动画与显示器的刷新率同步，提供更流畅的用户体验。
			性能优化：它避免了不必要的重绘和回流，提高了性能。
			广泛使用：许多现代 JavaScript 库和框架（如 React、Vue 等）内部也使用 requestAnimationFrame 来优化动画和渲染逻辑。
		requestAnimationFrame 的作用：
			确保动画与显示器刷新率同步，提供流畅的动画体验，并且在每一帧刷新时调用回调函数。
		Polyfill 的作用：
			为不支持某些现代 API 的浏览器提供兼容性支持，确保代码在不同环境中都能正常工作。
	*/
	// 若浏览器不支持原生的 requestAnimationFrame，则提供一个基于 setTimeout 的 polyfill 实现
	if (!window.requestAnimationFrame) {
		window.requestAnimationFrame = function (callback) {
			// 获取当前时间戳
			const currTime = Date.now();
			// 计算自上次调用 requestAnimationFrame 以来的时间间隔，单位：毫秒。
			const interval = currTime - lastTime;
			/*
				Math.max(0, 16.7 - interval)：
					Math.max(...args) ：
						接受任意数量的参数，并返回这些参数中的最大值。
						在这个表达式中，Math.max(0, ...) 用于确保 timeToCall 的值不会为负数。
						如果 timeToCall 是负数，它会被视为 0，即立即执行回调。
					如果 interval 小于 16.7 毫秒，
						16.7 - interval 会是一个正数，
						表示还需要等待多少毫秒才能达到 16.7 毫秒的间隔。★★★★★★★
					如果 interval 大于或等于 16.7 毫秒，
						16.7 - interval 会是一个非正数（0 或负数），
						此时 Math.max(0, ...) 会返回 0，表示不需要额外等待，立即执行回调。
			*/
			// 确保每次回调之间的最小间隔为 16.7ms（每秒60次刷新）
			const timeToCall = Math.max(0, 16.7 - interval);
			/*
				刷新间隔 16.7ms（每秒 60 次刷新）：
					16.7 毫秒是基于 60 帧每秒的刷新率计算得出的（1000 毫秒 / 60 ≈ 16.7 毫秒）。★★★★★★★
				表达式 `16.7 - interval` 计算的是：
					当前回调函数的本次与上次调用的时间间隔，距离期望的 16.7 毫秒，还差多少毫秒？★★★★★★★
					如果这个差值是正数，说明当前时间还没有达到期望的刷新间隔，需要等待这个差值的时间。
					如果这个差值是负数或零，说明当前时间已经超过了期望的刷新间隔，或者是第一次调用，不需要等待。

				1、通过 `Math.max(0, ...)` 确保这个差值不会为负数。
				2、通过 `setTimeout` 来确保符合期望的刷新间隔 16.7ms（每秒 60 次刷新）。
			*/
			// 设置一个定时器，在指定时间后调用回调函数，并传递时间间隔作为参数
			const id = window.setTimeout(function () {
				callback(interval);
			}, timeToCall);
			// 更新 lastTime 为下一次调用的时间戳
			lastTime = currTime + timeToCall;
			// 返回定时器 ID，以便可以取消该定时器
			return id;
		};
	}

	// 若浏览器不支持原生的 cancelAnimationFrame，则使用 clearTimeout 作为替代方案
	if (!window.cancelAnimationFrame) {
		window.cancelAnimationFrame = function (id) {
			// 传入定时器 ID，取消对应的定时器
			clearTimeout(id);
		};
	}
}
